/*
 * Created on Nov 26, 2009
 */
package com.mgmaps.utils;

import java.io.IOException;
import java.io.InputStream;
import javax.microedition.media.Manager;
import javax.microedition.media.MediaException;
import javax.microedition.media.Player;
import javax.microedition.media.PlayerListener;
  
  /**
 * Play sound in AsyncCalls thread.
 * 
 * @author CristiS
 * @version 1.0
 */
public class PlaySound extends IPlaySound implements PlayerListener {
  private String filename;
  private InputStream is;
  private Player pl;
  private IPlaySoundObserver obs;
  private IPlaySoundIO io;
  
  public ExtQueue q;
  private static final int MAX_QUEUE = 50;
  
  // quitting flag
  private boolean quitting;
  
  private static final String[] ENCODINGS = { "audio/mp3",
      "audio/mpeg", "audio/mpg", "audio/x-mpeg", "audio/x-mpg" };
  private static String lastEncoding;
  
  public PlaySound() {
    q = new ExtQueue(MAX_QUEUE);
    start();
  }

  /**
   * Called when the sound ends.
   * 
   * @param player
   *          current player object
   * @param event
   *          event that occured
   * @param eventData
   *          event data
   * @see javax.microedition.media.PlayerListener#playerUpdate(javax.microedition.media.Player,
   *      java.lang.String, java.lang.Object)
   */
  public void playerUpdate(Player player, String event, Object eventData) {
    if (event == null) {
      // Logger.debug("PLAYER UPDATE NULL");
      return;
    }
    else if (event.equals(PlayerListener.END_OF_MEDIA)) {
      // Logger.debug("Playing ended");
      if (obs != null)
        obs.playSoundEnded(filename);
    }
    else if (event.equals(PlayerListener.STOPPED)) {
      // Logger.debug("Playing stopped");
      if (obs != null)
        obs.playSoundEnded(filename);
    }
    else if (event.equals(PlayerListener.CLOSED)) {
      // Logger.debug("Playing closed");
      pl = null;
      return;
    }
    else if (event.equals(PlayerListener.ERROR))
      ExtLogger.error("Error playing");
    else {
      // Logger.debug("PLAYER UPDATE " + event);
      return;
    }

    /*
     * if (Settings.isNokia) { Settings.gpsSyncEnabled = oldGPSSync; Settings.vrEnabled = true; }
     */

    try {
      if (player != null && pl == player)
        player.close();
    }
    catch (Exception ex) {}

    pl = null;

    if (is != null)
      try {
        is.close();
      }
      catch (IOException ie) {}
    is = null;
  }
  
  public synchronized void setObserver(final IPlaySoundObserver obs) {
    this.obs = obs;
  }
  
  public synchronized void setIO(final IPlaySoundIO io) {
    this.io = io;
  }
  
  public synchronized void startPlay(final String fn) {
    q.push(new PlaySoundCommand(PlaySoundCommand.START, fn));
    notify();
  }
  
  private void readFileError() {
    // nothing to do, continue like it didn't happen
    if (obs != null)
      obs.playSoundFileNotFound(filename);
  }
  
  public synchronized boolean isQuitting() {
    return quitting;
  }
  
  public synchronized void notifyExit() {
    quitting = true;
    notify();
  }

  public void run() {
    quitting = false;
    is = null;
    PlaySoundCommand psc = null;
    while (!isQuitting()) {
      synchronized(this) {
        // is queue empty?
        if (q.isEmpty())
          try {
            wait();
          }
          catch (InterruptedException ie) {
          }
          
        // consume one element
        psc = (PlaySoundCommand) q.pop();
      }
      
      // process this object
      if (psc != null)
        processData(psc);
    }
  }
  
  private void processData(final PlaySoundCommand psc) {
    if (psc == null) {
      ExtLogger.error("Invalid command for playSound");
      return;
    }
    
    final int cmd = psc.getCommand();
    if (cmd == PlaySoundCommand.START) {
      // still playing? stop the old player first
      if (pl != null)
        processData(new PlaySoundCommand(PlaySoundCommand.STOP));
      
      filename = psc.getFilename();
      
      try {
        try {
          is = io.getInputStream(filename);
        }
        catch (Exception ex) {
          ExtLogger.handle(ex, false, "Error opening sound file " + filename);
          readFileError();
        }
      
        if (is == null) {
          ExtLogger.error("No sound to play");
          return;
        }
        
        // TODO use Manager.getSupportedContentTypes
        if (lastEncoding == null)
          for (int i = 0; i < ENCODINGS.length; i++)
            try {
              pl = Manager.createPlayer(is, ENCODINGS[i]);
              lastEncoding = ENCODINGS[i];
              break;
            }
            catch (MediaException me) {
              ExtLogger.debug("Format " + ENCODINGS[i] + " not supported.");
            }
        else
          pl = Manager.createPlayer(is, lastEncoding);
        
        if (pl == null) {
          ExtLogger.error("MP3 playing not supported");
          return;
        }
        
        pl.addPlayerListener(this);

        // Logger.debug("Playing trap sound for " + trapName);
        pl.realize();
        pl.prefetch();

        /*
        VolumeControl volumeControl = (VolumeControl) pl.getControl("VolumeControl");
        // Logger.debug("Audio volume is " + Settings.audioVolume);
        if (volumeControl != null)
        volumeControl.setLevel(100);
        */

        pl.start();
        // Logger.debug("Playing started");
      }
      catch (Exception ex) {
        ExtLogger.handle(ex, false, "Error playing sound");
        try {
          playerUpdate(null, PlayerListener.END_OF_MEDIA, null);
        }
        catch (Exception ex2) {
        }
        if (is != null) {
          try {
            is.close();
          }
          catch (IOException ie) {}
          is = null;
        }
      }
    } // start play
    
    else if (cmd == PlaySoundCommand.STOP) {
      filename = null;
      if (pl != null) {
        try {
          pl.stop();
        }
        catch (Exception ex) {
          ExtLogger.handle(ex, false, "Error stopping the play");
          try {
            playerUpdate(null, PlayerListener.END_OF_MEDIA, null);
          }
          catch (Exception ex2) {
          }
          if (is != null) {
            try {
              is.close();
            }
            catch (IOException ie) {}
            is = null;
          }
        }
        finally {
          if (pl != null) {
            try {
              pl.close();
            }
            catch (Exception ex) {
            }
            pl = null;
          }
        }
      }
    } // stop play
    
    // else invalid command
  }
  
  public synchronized void stopPlay() {
    q.push("2");
    notify();
  }
}
